package com.zbb.exam_system.service.Impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.luhuiguo.fastdfs.domain.StorePath;
import com.luhuiguo.fastdfs.service.FastFileStorageClient;
import com.zbb.exam_system.dto.TableDto;
import com.zbb.exam_system.entity.*;
import com.zbb.exam_system.exception.ExamException;
import com.zbb.exam_system.mapper.*;
import com.zbb.exam_system.service.PaperService;
import com.zbb.exam_system.service.QuestionService;
import com.zbb.exam_system.service.UserService;
import com.zbb.exam_system.utils.ExamUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.zbb.exam_system.constant.BusinessConstant.*;
import static com.zbb.exam_system.constant.RegexConstant.POSITIVE_INTEGER_REGEX;

/**
 * @author zbbstart
 * @date 2021/2/12 - 13:05
 */
@Service
@Slf4j
public class PaperServiceImpl implements PaperService {
    @Autowired
    private PaperMapper paperMapper;
    @Autowired
    private ExamMapper examMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RelationMapper relationMapper;
    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private ExamUtils examUtils;
    /**
     * 这里用的是一个三方对fdfs基于springboot的封装
     */
    @Autowired
    private FastFileStorageClient storageClient;


    /**
     * 根据老师编号获取试卷
     */
    @Override
    public TableDto getPapersByTeacherNo(Integer teacherNo, Integer pageNo, Integer size) {
        Page<Paper> page = new Page<>(pageNo, size);
        QueryWrapper<Paper> wrapper = new QueryWrapper<Paper>().eq("teacherNo", teacherNo);
        //分页查询
        Page<Paper> paperPage = paperMapper.selectPage(page, wrapper);
        //获取试卷列表
        List<Paper> paperList = paperPage.getRecords();
        if (CollectionUtils.isEmpty(paperList)) {
            return examUtils.setTableDto();
        }
        SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //重新赋予过滤条件后的试卷列表
        paperList.forEach(p -> {
            try {
                boolean flag = df2.parse(df2.format(new Date())).after(df2.parse(p.getExpire()));
                p.setExpire(flag ? "true" : "false");
            } catch (ParseException e) {
                e.printStackTrace();
            }
        });
        return examUtils.setTableDto(paperPage.getTotal(), paperList);
    }

    /**
     * 创建试卷
     */
    @Override
    public void createPaper(Paper paper) {
        if (!paper.getQuestionCount().toString().matches(POSITIVE_INTEGER_REGEX)) {
            throw new ExamException("题目的数量为正整数...");
        }
        //判断试卷的每题分数为正整数
        if (!paper.getScorePerQuestion().toString().matches(POSITIVE_INTEGER_REGEX)) {
            throw new ExamException("每题分值为正整数...");
        }
        //判断试卷的考试时长（分钟）为正整数
        if (!paper.getExamDuration().toString().matches(POSITIVE_INTEGER_REGEX)) {
            throw new ExamException("考试时长（分钟）为正整数...");
        }
        //判断到期时间不能为空，且不能等于当前时间
        if (null == paper.getExpire()) {
            throw new ExamException("到期时间不能为空...");
        }
        //设置当前试卷的总分
        paper.setTotalScore(paper.getQuestionCount() * paper.getScorePerQuestion());
        //设置日期格式1
        SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd");
        // new Date()为获取当前系统时间
        String curDateString = df1.format(new Date());
        SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // new Date()为获取当前系统时间
        String curTime = df2.format(new Date());
        try {
            Date curDate = df2.parse(curTime);
            Date endDate = df2.parse(paper.getExpire());
            if (curDate.after(endDate)) {
                throw new ExamException("到期时间不能早于当前时间...");
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        //创建默认状态是未生成考试试卷
        paper.setStatus(0);
        //写入数据库
        log.info("试卷写入的所有信息是:" + paper);
        paperMapper.insert(paper);
        Integer paperId = paper.getPaperId();
        log.info("创建的试卷编号是:" + paperId);
        //新增完毕后插入当前试卷的paperNo 为当前试卷补零3
        String s = curDateString.replaceAll("-", "");
        String paperNo = s + examUtils.zeroFillThree(paperId);
        paperMapper.update(new Paper(), new UpdateWrapper<Paper>()
                .eq("paperId", paperId)
                .set("paperNo", paperNo));
    }

    /**
     * 通过试卷编号获取试卷信息
     */
    @Override
    public Paper getPaperInfoByPaperId(Integer paperId) {
        return paperMapper.selectById(paperId);
    }

    /**
     * 生成试卷
     */
    @Override
    public void generatePaper(Integer paperId) {
        //根据试卷编号获取当前试卷的所有信息
        Paper paper = getPaperInfoByPaperId(paperId);
        SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //判断当前试卷是否过期
        try {
            if (df2.parse(df2.format(new Date())).after(df2.parse(paper.getExpire()))) {
                throw new ExamException("当前试卷已过期,不能生成试卷...");
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        //判断当前的试卷编号是否生成过试卷
        if (1 == paper.getStatus()) {
            throw new ExamException("已经生成过试卷...");
        }
        //获取当前试卷的总题目数
        Integer questionCount = paper.getQuestionCount();
        //获取题库中的所有题目数量
        Integer count = questionMapper.selectCount(new QueryWrapper<Question>());
        //判断题目中的题目是否满足试卷题目的要求
        if (count < questionCount) {
            throw new ExamException("题库中的题目不足...");
        }
        //获取所有的题目随机为当前试卷的总题目数
        List<Question> randomTenData = questionMapper.getRandomData(questionCount);
        //将随机获取的集合赋值给需要导出为word文件的数据源集合
        List<QuestionEntity> questionEntityList = new ArrayList<>();

        for (Question question : randomTenData) {
            try {
                QuestionEntity questionEntity = new QuestionEntity();
                BeanUtils.copyProperties(questionEntity, question);
                questionEntityList.add(questionEntity);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        //导出
        Workbook workbook = ExcelExportUtil.exportExcel(
                //表格的大标题和当前sheet的名称
                new ExportParams(paper.getPaperNo(), "试卷"),
                //数据列表中的实体类的类型
                QuestionEntity.class,
                //要输出的数据列表
                questionEntityList);
        String filePath = "";
        try {
            filePath = PAPER_FILENAME_PREFIX + File.separator + paper.getPaperNo() +PAPER_XIS_SUFFIX;
            // demo workbook.write(new FileOutputStream("e:" + File.separator + "1.xls"));
            workbook.write(new FileOutputStream(filePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        //原.docx后缀
        //String fileName = paper.getPaperNo() + PAPER_SUFFIX;
        //这里改成了Excel的.xls后缀
        String fileName = paper.getPaperNo() + PAPER_XIS_SUFFIX;
        String extName = FilenameUtils.getExtension(fileName);
        //原始
        //StorePath storePath = storageClient.uploadFile(file.toString().getBytes(), extName);

        //更改
        byte[] bytes = new byte[0];
        try {
            bytes = FileUtils.readFileToByteArray(new File(fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        StorePath storePath = storageClient.uploadFile(bytes, extName);
        String fullPath = storePath.getFullPath();
        //将文件位置，全路径写入数据库
        log.info(fullPath);
        paperMapper.update(new Paper(), new UpdateWrapper<Paper>()
                .eq("paperId", paperId)
                .set("fullPath", fullPath)
                .set("fileName", fileName)
                .set("status", 1));

    }

    @Override
    public Paper getPaperAllInfo(Integer paperId) {
        //获取当前试卷宏观信息
        Paper paper = paperMapper.selectById(paperId);
        //判断是否生成了
        String fileName = paper.getFileName();
        if (null == fileName) {
            throw new ExamException("请先生成试卷再预览...");
        }
        //获取生成的word文件路径
        String filePath = PAPER_FILENAME_PREFIX + File.separator + fileName;
        //获取当前试卷的所有题目
        ImportParams params = new ImportParams();
        params.setTitleRows(1);
        params.setHeadRows(1);
        List<QuestionEntity> list = ExcelImportUtil.importExcel(new File(filePath), QuestionEntity.class, params);
        //将导出的数据写入试卷对象中
        paper.setQuestionList(list);
        return paper;
    }

    @Override
    public Paper getPaperByExamNo(String examNo) {
        return paperMapper.selectOne(new QueryWrapper<Paper>().eq("paperNo", examNo));
    }

    @Override
    public void cancelPaper(Integer paperId) {
        SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //将试卷的时间设置为当前时间，当获取试卷时会自动将expire变为true
        String curTime = df2.format(new Date());
        paperMapper.update(new Paper(), new UpdateWrapper<Paper>()
                .eq("paperId", paperId)
                .set("expire", curTime));
        String examNo = paperMapper.selectById(paperId).getPaperNo();
        examMapper.update(new Exam(), new UpdateWrapper<Exam>()
                .eq("examNo", examNo)
                //0,未生成，1,已生成未过期,2已过期
                .set("examStatus", 2));
    }

    @Override
    public void deletePaper(Integer paperId) {
        //获得paperNo(examNo) ,删除exam表中相应的数据
        String examNo = paperMapper.selectById(paperId).getPaperNo();
        examMapper.delete(new QueryWrapper<Exam>().eq("examNo", examNo));
        //删除paper表中的相关数据
        paperMapper.deleteById(paperId);
    }

    /**
     * 考试结果统计下载
     */
    @Override
    public void examResultDownload(Integer paperId){
        Paper paper = paperMapper.selectById(paperId);
        if (null == paper){
            throw new ExamException("不存在该试卷...");
        }
        //老师编号
        Integer teacherNo = paper.getTeacherNo();
        //所属该老师下的学生
        List<Integer> allStudentNos = relationMapper.selectList(new QueryWrapper<Relation>()
                .eq("teacherNo", teacherNo))
                .stream()
                .map(Relation::getStudentNo)
                .collect(Collectors.toList());
        //试卷编号
        String paperNo = paper.getPaperNo();
        //获取考生考试信息
        List<Exam> examList = examMapper.selectList(new QueryWrapper<Exam>().eq("examNo", paperNo).eq("examStatus",2));
        if (CollectionUtils.isEmpty(examList)){
            throw new ExamException("还没有学生进行该试卷的考试...");
        }
        //考生编号
        List<Integer> partStudentNos = examList.stream().map(Exam::getStudentNo).collect(Collectors.toList());
        //考试姓名
        List<User> usernames = userMapper.selectBatchIds(partStudentNos);
        //未考试人数编号集合
        List<Integer> differents = examUtils.getDifferents(allStudentNos, partStudentNos);
        //最高分，最低分，平均分
        Integer max = examMapper.getMaxOrMin(paperNo, "desc");
        Integer min = examMapper.getMaxOrMin(paperNo, "asc");
        Integer average = examMapper.getAverage(paperNo);
        //需要导入的数据集合
        List<PaperExamResult> results = new ArrayList<>();
        for (int i = 0;i<partStudentNos.size();i++){
            PaperExamResult result = new PaperExamResult();
            if (i == 0){
                result.setPaperNo(paperNo);
                result.setQuestionGrade(paper.getScorePerQuestion());
                result.setQuestionContent(paper.getQuestionCount());
                result.setTotalGrade(paper.getTotalScore());
                result.setPeopleShould(allStudentNos.size());
                result.setPeopleReal(partStudentNos.size());
                result.setMaxGrade(max);
                result.setMinGrade(min);
                result.setAverageGrade(average);
            }
        }


    }

}
